pub use super::_entities::catalog_translations::{self, ActiveModel, Entity, Model};
pub(crate) use crate::models::_entities::catalog_translations::CreateCatalogTranslationParams;
use loco_rs::prelude::*;
use serde::Deserialize;
use uuid::Uuid;

#[derive(Debug, Validate, Deserialize)]
pub struct Validator {
    #[validate(length(min = 1, message = "Catalog ID must not be empty."))]
    pub catalog_id: String,
    #[validate(length(min = 2, message = "Locale must be at least 2 characters long."))]
    pub locale: String,
    #[validate(length(min = 1, message = "Title must not be empty."))]
    pub title: String,
}

impl Validatable for ActiveModel {
    fn validator(&self) -> Box<dyn Validate> {
        Box::new(Validator {
            catalog_id: self.catalog_id.as_ref().to_owned(),
            locale: self.locale.as_ref().to_owned(),
            title: self.title.as_ref().to_owned(),
        })
    }
}

#[async_trait::async_trait]
impl ActiveModelBehavior for ActiveModel {
    async fn before_save<C>(self, _db: &C, insert: bool) -> Result<Self, DbErr>
    where
        C: ConnectionTrait,
    {
        self.validate()?;
        if insert {
            let mut this = self;
            this.id = Set(Uuid::new_v4().to_string());
            Ok(this)
        } else {
            Ok(self)
        }
    }
}

impl Model {
    /// finds a catalog translation by the provided id
    ///
    /// # Errors
    ///
    /// When could not find catalog translation or DB query error
    pub async fn find_by_id(db: &DatabaseConnection, id: &str) -> ModelResult<Self> {
        let catalog_translation = catalog_translations::Entity::find()
            .filter(
                model::query::condition()
                    .eq(catalog_translations::Column::Id, id)
                    .build(),
            )
            .one(db)
            .await?;
        catalog_translation.ok_or_else(|| ModelError::EntityNotFound)
    }

    /// finds catalog translations by the provided catalog id
    ///
    /// # Errors
    ///
    /// When could not find catalog translations or DB query error
    pub async fn find_by_catalog_id(
        db: &DatabaseConnection,
        catalog_id: &str,
    ) -> ModelResult<Vec<Self>> {
        let catalog_translations = catalog_translations::Entity::find()
            .filter(
                model::query::condition()
                    .eq(catalog_translations::Column::CatalogId, catalog_id)
                    .build(),
            )
            .all(db)
            .await?;
        Ok(catalog_translations)
    }

    /// finds a catalog translation by the provided catalog id and locale
    ///
    /// # Errors
    ///
    /// When could not find catalog translation or DB query error
    pub async fn find_by_catalog_id_and_locale(
        db: &DatabaseConnection,
        catalog_id: &str,
        locale: &str,
    ) -> ModelResult<Self> {
        let catalog_translation = catalog_translations::Entity::find()
            .filter(
                model::query::condition()
                    .eq(catalog_translations::Column::CatalogId, catalog_id)
                    .eq(catalog_translations::Column::Locale, locale)
                    .build(),
            )
            .one(db)
            .await?;
        catalog_translation.ok_or_else(|| ModelError::EntityNotFound)
    }

    /// Asynchronously creates a catalog translation and saves it to the database.
    ///
    /// # Errors
    ///
    /// When could not save the catalog translation into the DB
    pub async fn create(db: &DatabaseConnection, params: &CreateCatalogTranslationParams) -> ModelResult<Self> {
        let txn = db.begin().await?;

        if catalog_translations::Entity::find()
            .filter(
                query::condition()
                    .eq(catalog_translations::Column::CatalogId, &params.catalog_id)
                    //.eq(catalog_translations::Column::Locale, &params.locale)
                    .build(),
            )
            .one(&txn)
            .await?
            .is_some()
        {
            return Err(ModelError::EntityAlreadyExists {});
        }

        let catalog_translation = ActiveModel {
            catalog_id: ActiveValue::set(params.catalog_id.to_string()),
            locale: ActiveValue::set(params.locale.clone().unwrap_or_default()),
            title: ActiveValue::set(params.title.to_string()),
            summary: ActiveValue::set(params.summary.clone()),
            meta_title: ActiveValue::set(params.meta_title.clone()),
            meta_description: ActiveValue::set(params.meta_description.clone()),
            meta_keywords: ActiveValue::set(params.meta_keywords.clone()),
            ..Default::default()
        }
        .insert(&txn)
        .await?;

        txn.commit().await?;

        Ok(catalog_translation)
    }
}

impl ActiveModel {

    pub async fn update_title(
         self,
        db: &DatabaseConnection,
        title: String,
    ) -> ModelResult<Model> {
        let mut am: ActiveModel = self.into();
        am.title = ActiveValue::set(title);
        Ok(am.update(db).await?)
    }

    pub async fn update_meta_title(
         self,
        db: &DatabaseConnection,
        meta_title: Option<String>,
    ) -> ModelResult<Model> {
        let mut am: ActiveModel = self.into();
        am.meta_title = ActiveValue::set(meta_title);
        Ok(am.update(db).await?)
    }

    pub async fn update_meta_info(
         self,
        db: &DatabaseConnection,
        meta_title: Option<String>,
        meta_description: Option<String>,
        meta_keywords: Option<String>,
    ) -> ModelResult<Model> {
        let mut am: ActiveModel = self.into();
        am.meta_title = ActiveValue::set(meta_title);
        am.meta_description = ActiveValue::set(meta_description);
        am.meta_keywords = ActiveValue::set(meta_keywords);
        Ok(am.update(db).await?)
    }
}
